/*
 * Copyright 2013 Krzysztof Smigielski
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.nilcaream.dirstore.core.processor;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.nilcaream.dirstore.core.common.*;
import com.nilcaream.dirstore.core.model.Archive;
import com.nilcaream.dirstore.core.model.ArchiveElement;
import com.nilcaream.dirstore.core.model.TrimmedElement;
import org.slf4j.Logger;

import javax.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import static com.nilcaream.dirstore.core.common.Utils.*;

/**
 * TODO
 * <p/>
 * Krzysztof Smigielski 7/7/13 8:21 PM
 */
public class ZipTrimmer implements ArchiveCreator {

    @Inject
    private ZipTool zipTool;

    @Inject
    private TypeAwareTrimmer trimmer;

    @Inject
    private ArchiveMapper archiveMapper;

    @Inject
    private ProgressMeter progressMeter;

    @Inject
    private Logger logger;

    private final TrimmedElement empty = createEmpty();

    @Override
    public Archive createArchive(Path input) throws IOException {
        Preconditions.checkArgument(input.toFile().isFile(), input.toString() + " is not a file");
        try (ZipFile zipFile = new ZipFile(input.toFile(), ZipFile.OPEN_READ, Charsets.UTF_8)) {
            return toArchive(zipFile);
        }
    }

    public void createTrimmedZip(Path input, OutputStream output, boolean quickMode) throws IOException {
        progressMeter.initialize(input);
        long totalSize = 0;
        try (ZipOutputStream zipOutputStream = zipTool.createZipOutput(output)) {
            ZipFile zipFile = new ZipFile(input.toFile(), ZipFile.OPEN_READ, Charsets.UTF_8);
            for (ZipEntry zipEntry : getEntries(zipFile)) {
                TrimmedElement trimmedElement = empty;
                if (!zipEntry.isDirectory()) {
                    if (!quickMode) {
                        try (InputStream inputStream = zipFile.getInputStream(zipEntry)) {
                            trimmedElement = trimmer.trim(inputStream, zipEntry.getName());
                        }
                    }
                    totalSize += zipEntry.getSize();
                }
                progressMeter.increment(input);
                zipTool.add(zipEntry, trimmedElement, zipOutputStream);
            }
            zipOutputStream.setComment(toReadableFileSize(totalSize));
        }
    }

    private Archive toArchive(ZipFile zipFile) {
        Archive archive = new Archive();
        archive.setElements(Lists.<ArchiveElement>newArrayListWithCapacity(zipFile.size() + 10));
        ZipEntry lastEntry = null;
        try {
            for (ZipEntry zipEntry : getEntries(zipFile)) {
                lastEntry = zipEntry;
                archive.getElements().add(archiveMapper.createFrom(zipEntry, null));
            }
        } catch (Exception e) {
            logger.warn("last read zip entry: " + lastEntry);
            throw Throwables.propagate(e);
        }
        return archive;
    }
}
